﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace PatcherYRpp
{
    [StructLayout(LayoutKind.Sequential)]
    public struct PassengersClass
    {
        public int NumPassengers;
        public IntPtr firstPassenger;
        public Pointer<FootClass> FirstPassenger { get => firstPassenger; set => firstPassenger = value; }

        public unsafe void AddPassenger(Pointer<FootClass> pPassenger)
        {
            var func = (delegate* unmanaged[Thiscall]<ref PassengersClass, IntPtr, void>)0x4733A0;
            func(ref this, pPassenger);
        }

        public unsafe Pointer<FootClass> GetFirstPassenger()
        {
            return this.FirstPassenger;
        }

        public unsafe Pointer<FootClass> RemoveFirstPassenger()
        {
            var func = (delegate* unmanaged[Thiscall]<ref PassengersClass, IntPtr>)0x473430;
            return func(ref this);
        }

        public unsafe int GetTotalSize()
        {
            var func = (delegate* unmanaged[Thiscall]<ref PassengersClass, int>)0x473460;
            return func(ref this);
        }

        public unsafe int IndexOf(Pointer<FootClass> candidate)
        {
            var func = (delegate* unmanaged[Thiscall]<ref PassengersClass, IntPtr, int>)0x473500;
            return func(ref this, candidate);
        }
    }


    [StructLayout(LayoutKind.Explicit, Size = 4)]
    public struct VeterancyStruct
    {
        [FieldOffset(0)] public float Veterancy;

        public bool IsElite()
        {
            return Veterancy >= 2.0f;
        }

        public bool IsVeteran()
        {
            return Veterancy >= 1.0f && Veterancy < 2.0f;
        }

        public bool IsRookie()
        {
            return Veterancy >= 0.0f && Veterancy < 1.0f;
        }

        public bool IsNegative()
        {
            return Veterancy < 0.0f;
        }

        public void Reset()
        {
            Veterancy = 0.0f;
        }

        public void SetRookie(bool notReally = true)
        {
            Veterancy = notReally ? -0.25f : 0.0f;
        }

        public void SetVeteran(bool notReally = true)
        {
            Veterancy = notReally ? 1.0f : 0.0f;
        }

        public void SetElite(bool notReally = true)
        {
            Veterancy = notReally ? 2.0f : 0.0f;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct FlashData
    {
        public int DurationRemaining;
        public Bool FlashingNow;

        public unsafe bool Update()
        {
            var func = (delegate* unmanaged[Thiscall]<ref FlashData, Bool>)0x4CC770;
            return func(ref this);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct OwnedTiberiumStruct
    {
        public unsafe float GetAmount(int index)
        {
            var func = (delegate* unmanaged[Thiscall]<ref OwnedTiberiumStruct, int, float>)0x6C9680;
            return func(ref this, index);
        }

        public unsafe float GetTotalAmount()
        {
            var func = (delegate* unmanaged[Thiscall]<ref OwnedTiberiumStruct, float>)0x6C9650;
            return func(ref this);
        }

        public unsafe float AddAmount(float amount, int index)
        {
            var func = (delegate* unmanaged[Thiscall]<ref OwnedTiberiumStruct, float, int, float>)0x6C9690;
            return func(ref this, amount, index);
        }

        public unsafe float RemoveAmount(float amount, int index)
        {
            var func = (delegate* unmanaged[Thiscall]<ref OwnedTiberiumStruct, float, int, float>)0x6C96B0;
            return func(ref this, amount, index);
        }

        public unsafe int GetTotalValue()
        {
            var func = (delegate* unmanaged[Thiscall]<ref OwnedTiberiumStruct, int>)0x6C9600;
            return func(ref this);
        }

        public float Tiberium1;
        public float Tiberium2;
        public float Tiberium3;
        public float Tiberium4;
    }



    [StructLayout(LayoutKind.Explicit, Size = 1312)]
    public struct TechnoClass
    {
        static public readonly IntPtr ArrayPointer = new IntPtr(0xA8EC78);
        static public ref DynamicVectorClass<Pointer<TechnoClass>> Array { get => ref DynamicVectorClass<Pointer<TechnoClass>>.GetDynamicVector(ArrayPointer); }

        public unsafe Pointer<TechnoTypeClass> Type
        {
            get
            {
                var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, IntPtr>)0x6F3270;
                return func(ref this);
            }
        }


        public unsafe int SelectWeapon(Pointer<AbstractClass> pTarget)
        {
            var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, IntPtr, int>)
                Helpers.GetVirtualFunctionPointer(Pointer<TechnoClass>.AsPointer(ref this), 185);
            return func(ref this, pTarget);
        }

        public unsafe int GetROF(int idxWeapon)
        {
            var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, int, int>)
                Helpers.GetVirtualFunctionPointer(Pointer<TechnoClass>.AsPointer(ref this), 198);
            return func(ref this, idxWeapon);
        }

        public unsafe FireError GetFireErrorWithoutRange(Pointer<AbstractClass> pTarget, int weaponIndex)
        {
            var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, IntPtr, int, FireError>)this.GetVirtualFunctionPointer(239);
            return func(ref this, pTarget, weaponIndex);
        }

        public unsafe FireError GetFireError(Pointer<AbstractClass> pTarget, int weaponIndex, Bool checkRange)
        {
            var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, IntPtr, int, Bool, FireError>)this.GetVirtualFunctionPointer(240);
            return func(ref this, pTarget, weaponIndex, checkRange);
        }


        public unsafe Pointer<WeaponStruct> GetWeapon(int idxWeapon)
        {
            var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, int, IntPtr>)
                Helpers.GetVirtualFunctionPointer(Pointer<TechnoClass>.AsPointer(ref this), 254);
            return func(ref this, idxWeapon);
        }

        public unsafe void SetTarget(Pointer<AbstractClass> pTarget)
        {
            var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, IntPtr, void>)Helpers.GetVirtualFunctionPointer(Pointer<TechnoClass>.AsPointer(ref this), 242);
            func(ref this, pTarget);
        }

        public unsafe Pointer<BulletClass> Fire(Pointer<AbstractClass> pTarget, int idxWeapon)
        {
            var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, IntPtr, int, IntPtr>)this.GetVirtualFunctionPointer(243);
            return func(ref this, pTarget, idxWeapon);
        }

        public unsafe Pointer<BulletClass> Fire_NotVirtual(Pointer<AbstractClass> pTarget, int idxWeapon)
        {
            var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, IntPtr, int, IntPtr>)0x6FDD50;
            return func(ref this, pTarget, idxWeapon);
        }


        public unsafe void SetDestination(Pointer<CellClass> pCell, bool unknow)
        {
            var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, IntPtr, Bool, void>)Helpers.GetVirtualFunctionPointer(Pointer<TechnoClass>.AsPointer(ref this), 288);
            func(ref this, pCell, unknow);
        }

        public unsafe void SetFocus(Pointer<AbstractClass> pTarget)
        {
            var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, IntPtr, void>)0x70C610;
            func(ref this, pTarget);
        }

        public unsafe void EnteredOpenTopped(Pointer<TechnoClass> pWho)
        {
            var func = (delegate* unmanaged[Thiscall]<ref TechnoClass, IntPtr, void>)0x710470;
            func(ref this, pWho);
        }


        [FieldOffset(0)] public ObjectClass Base;


        [FieldOffset(240)] public FlashData Flashing;
        [FieldOffset(248)] public ProgressTimer Animation; // how the unit animates
        [FieldOffset(276)] public PassengersClass Passengers;

        [FieldOffset(284)] public IntPtr transporter; // unit carrying me
        public Pointer<TechnoClass> Transporter { get => transporter; set => transporter = value; }

        [FieldOffset(336)] public VeterancyStruct Veterancy;


        [FieldOffset(460)] public IntPtr drainTarget; // eg Disk -> PowerPlant, this points to PowerPlant
        public Pointer<TechnoClass> DrainTarget { get => drainTarget; set => drainTarget = value; }
        [FieldOffset(464)] public IntPtr drainingMe; // eg Disk -> PowerPlant, this points to Disk
        public Pointer<TechnoClass> DrainingMe { get => drainingMe; set => drainingMe = value; }
        [FieldOffset(468)] public IntPtr drainAnim;
        public Pointer<AnimClass> DrainAnim { get => drainAnim; set => drainAnim = value; }

        [FieldOffset(540)] public IntPtr owner;
        public Pointer<HouseClass> Owner { get => owner; set => owner = value; }

        [FieldOffset(664)] public Bool Berzerk;

        [FieldOffset(692)] public Pointer<AbstractClass> Target; //if attacking
        [FieldOffset(696)] public Pointer<AbstractClass> LastTarget;

        [FieldOffset(684)] public IntPtr locomotorTarget; // mag->LocoTarget = victim
        public Pointer<FootClass> LocomotorTarget { get => locomotorTarget; set => locomotorTarget = value; }

        [FieldOffset(688)] public IntPtr locomotorSource; // victim->LocoSource = mag
        public Pointer<FootClass> LocomotorSource { get => locomotorSource; set => locomotorSource = value; }

        [FieldOffset(720)] public IntPtr spawnManager;
        public Pointer<SpawnManagerClass> SpawnManager { get => spawnManager; set => spawnManager = value; }

        [FieldOffset(724)] public IntPtr spawnOwner;
        public Pointer<TechnoClass> SpawnOwner { get => spawnOwner; set => spawnOwner = value; }

        [FieldOffset(764)] public int Ammo;

        [FieldOffset(828)] public OwnedTiberiumStruct Tiberium;

        [FieldOffset(880)] public FacingStruct BarrelFacing;
        [FieldOffset(904)] public FacingStruct Facing;
        [FieldOffset(928)] public FacingStruct TurretFacing;
        [FieldOffset(952)] public int CurrentBurstIndex;

        [FieldOffset(972)]
        public Bool CountedAsOwned; // is this techno contained in OwningPlayer->Owned... counts?

        [FieldOffset(981)]
        public Bool IsInPlayfield; 
        
    }
}
